package cn.jingyuan.bee.utils.io;

import cn.jingyuan.bee.utils.CharsetUtils;
import cn.jingyuan.bee.utils.StringUtils;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

/**
 * {@link ByteBuffer} 工具类<br>
 * 此工具来自于 t-io 项目以及其它项目的相关部分收集<br>
 * ByteBuffer 的相关介绍见：https://www.cnblogs.com/ruber/p/6857159.html
 */
public class BufferUtils {

    /**
     * 拷贝到一个新的 ByteBuffer
     *
     * @param src 源 ByteBuffer
     * @param start 起始位置（包括）
     * @param end 结束位置（不包括）
     *
     * @return 新的 ByteBuffer
     */
    public static ByteBuffer copy(ByteBuffer src, int start, int end) {
        return copy(src, ByteBuffer.allocate(end - start));
    }

    /**
     * 拷贝 ByteBuffer
     *
     * @param src 源 ByteBuffer
     * @param dest 目标 ByteBuffer
     *
     * @return 目标 ByteBuffer
     */
    public static ByteBuffer copy(ByteBuffer src, ByteBuffer dest) {
        return copy(src, dest, Math.min(src.limit(), dest.remaining()));
    }

    /**
     * 拷贝 ByteBuffer
     *
     * @param src 源 ByteBuffer
     * @param dest 目标 ByteBuffer
     * @param length 长度
     *
     * @return 目标 ByteBuffer
     */
    public static ByteBuffer copy(ByteBuffer src, ByteBuffer dest, int length) {
        return copy(src, src.position(), dest, dest.position(), length);
    }

    /**
     * 拷贝 ByteBuffer
     *
     * @param src 源 ByteBuffer
     * @param srcStart 源开始的位置
     * @param dest 目标 ByteBuffer
     * @param destStart 目标开始的位置
     * @param length 长度
     *
     * @return 目标 ByteBuffer
     */
    public static ByteBuffer copy(ByteBuffer src, int srcStart, ByteBuffer dest, int destStart, int length) {
        System.arraycopy(src.array(), srcStart, dest.array(), destStart, length);
        return dest;
    }

    /**
     * 读取剩余部分并转为 UTF-8 编码字符串
     *
     * @param buffer ByteBuffer
     *
     * @return 字符串
     */
    public static String readUtf8Str(ByteBuffer buffer) {
        return readStr(buffer, CharsetUtils.CHARSET_UTF_8);
    }

    /**
     * 读取剩余部分并转为字符串
     *
     * @param buffer ByteBuffer
     * @param charset 编码
     *
     * @return 字符串
     */
    public static String readStr(ByteBuffer buffer, Charset charset) {
        return StringUtils.string(readBytes(buffer), charset);
    }

    /**
     * 读取剩余部分 bytes<br>
     *
     * @param buffer ByteBuffer
     *
     * @return bytes
     */
    public static byte[] readBytes(ByteBuffer buffer) {
        final int remaining = buffer.remaining();
        byte[] ab = new byte[remaining];
        buffer.get(ab);
        return ab;
    }

    /**
     * 读取指定长度的 bytes<br>
     * 如果长度不足，则读取剩余部分，此时 buffer 必须为读模式
     *
     * @param buffer ByteBuffer
     * @param maxLength 最大长度
     *
     * @return bytes
     */
    public static byte[] readBytes(ByteBuffer buffer, int maxLength) {
        final int remaining = buffer.remaining();
        if (maxLength > remaining) {
            maxLength = remaining;
        }
        byte[] ab = new byte[maxLength];
        buffer.get(ab);
        return ab;
    }

    /**
     * 读取指定区间的数据
     *
     * @param buffer {@link ByteBuffer}
     * @param start 开始位置
     * @param end 结束位置
     *
     * @return bytes
     */
    public static byte[] readBytes(ByteBuffer buffer, int start, int end) {
        byte[] bs = new byte[end - start];
        System.arraycopy(buffer.array(), start, bs, 0, bs.length);
        return bs;
    }

    /**
     * 一行的末尾位置，查找位置时位移 ByteBuffer 到结束位置
     *
     * @param buffer {@link ByteBuffer}
     *
     * @return 末尾位置，未找到或达到最大长度返回-1
     */
    public static int lineEnd(ByteBuffer buffer) {
        return lineEnd(buffer, buffer.remaining());
    }

    /**
     * 一行的末尾位置，查找位置时位移 ByteBuffer 到结束位置<br>
     * 支持的换行符如下：
     *
     * <pre>
     * 1. \r\n
     * 2. \n
     * </pre>
     *
     * @param buffer {@link ByteBuffer}
     * @param maxLength 读取最大长度
     *
     * @return 末尾位置，未找到或达到最大长度返回-1
     */
    public static int lineEnd(ByteBuffer buffer, int maxLength) {
        int primitivePosition = buffer.position();
        boolean canEnd = false;
        int charIndex = primitivePosition;
        byte b;
        while (buffer.hasRemaining()) {
            b = buffer.get();
            charIndex++;
            if (b == StringUtils.C_CR) {
                canEnd = true;
            } else if (b == StringUtils.C_LF) {
                return canEnd ? charIndex - 2 : charIndex - 1;
            } else {
                // 只有\r 无法确认换行
                canEnd = false;
            }

            if (charIndex - primitivePosition > maxLength) {
                // 查找到尽头，未找到，还原位置
                buffer.position(primitivePosition);
                throw new IndexOutOfBoundsException(StringUtils.format("Position is out of maxLength: {}", maxLength));
            }
        }

        // 查找到 buffer 尽头，未找到，还原位置
        buffer.position(primitivePosition);
        // 读到结束位置
        return -1;
    }

    /**
     * 读取一行，如果 buffer 中最后一部分并非完整一行，则返回 null<br>
     * 支持的换行符如下：
     *
     * <pre>
     * 1. \r\n
     * 2. \n
     * </pre>
     *
     * @param buffer ByteBuffer
     * @param charset 编码
     *
     * @return 一行
     */
    public static String readLine(ByteBuffer buffer, Charset charset) {
        final int startPosition = buffer.position();
        final int endPosition = lineEnd(buffer);

        if (endPosition > startPosition) {
            byte[] bs = readBytes(buffer, startPosition, endPosition);
            return StringUtils.string(bs, charset);
        } else if (endPosition == startPosition) {
            return StringUtils.EMPTY;
        }

        return null;
    }

    /**
     * 创建新 Buffer
     *
     * @param data 数据
     *
     * @return {@link ByteBuffer}
     */
    public static ByteBuffer create(byte[] data) {
        return ByteBuffer.wrap(data);
    }

    /**
     * 从字符串创建新 Buffer
     *
     * @param data 数据
     * @param charset 编码
     *
     * @return {@link ByteBuffer}
     */
    public static ByteBuffer create(CharSequence data, Charset charset) {
        return create(StringUtils.bytes(data, charset));
    }

    /**
     * 从字符串创建新 Buffer，使用 UTF-8 编码
     *
     * @param data 数据
     *
     * @return {@link ByteBuffer}
     */
    public static ByteBuffer createUtf8(CharSequence data) {
        return create(StringUtils.utf8Bytes(data));
    }

}
